/****************/
/* Jiahui Shi	*/
/* 05/28/2011	*/
/* CS 244B	*/
/* Spring 2011	*/
/****************/

#define DEBUG

#include "client.h"

/* ------------------------------------------------------------------ */


int
InitReplFs( unsigned short portNum, int packetLoss ) {
#ifdef DEBUG
    printf( "InitReplFs: Port number %d, packet loss %d percent\n", 
    portNum, packetLoss );
#endif

/****************************************************/
/* Initialize network access, local state, etc.     */
/****************************************************/
    netInit(&myAddr, &my_socket, &groupAddr, portNum);
    printf("my IP: %d\n", myAddr.sin_addr.s_addr);
    myIP = myAddr.sin_addr.s_addr;

    serverNum = 0;
    my_state = INIT_STATE;
    PACK_LOST_RATE = packetLoss;
    return( NormalReturn );  
}

void ConvertIncoming(ShortPacket *pack)
{
  pack->header.checksum = ntohs(pack->header.checksum);
  pack->header.paylen = ntohl(pack->header.paylen);
  pack->header.seqnum = ntohl(pack->header.seqnum);
  pack->header.type = ntohs(pack->header.type);
  
  switch(pack->header.type) {
// 	case JOIN_REQUEST_TYPE:
// 	{
// 	  JoinRequestPack *packX = (JoinRequestPack *) &pack->body;
// 	  packX->len = ntohs(packX->len);
// 	  break;
// 	}
// 	
// 	case JOIN_RESPONSE_TYPE:
// 	{
// 	  JoinResponsePack *packX = (JoinResponsePack *) &pack->body;
// 	  packX->len = ntohs(packX->len);
// 	  packX->player_count = ntohs(packX->player_count);
// 	  packX->score = ntohl(packX->score);
// 	  packX->target_id = ntohl(packX->target_id);
// 	  packX->xyd = ntohs(packX->xyd);
// 	  break;
// 	}
// 	
// 	case UPDATE_TYPE:
// 	{
// 	  UpdatePack *packX = (UpdatePack *) &pack->body;
// 	  packX->score = ntohl(packX->score);
// 	  packX->xyd = ntohs(packX->xyd);
// 	  break;
// 	}
// 	  
// 	case TAG_TYPE:
// 	{
// 	  TagPack *packX = (TagPack *) &pack->body;
// 	  packX->target_id = ntohl(packX->target_id);
// 	  break;
// 	}
// 	
// 	case TAG_RESPONSE_TYPE:
// 	{
// 	  TagResponsePack *packX = (TagResponsePack *) &pack->body;
// 	  packX->attacker_id = ntohl(packX->attacker_id);
// 	  packX->score = ntohl(packX->score);
// 	  packX->xyd = ntohs(packX->xyd);
// 	  break;
// 	}
  }
}


void processPacket (MWEvent *eventPacket)
{
//   MW244BPacket *pack = eventPacket->eventDetail;
//   
//   if (!checkSumRight(*pack)) return;
//   if (pack->id == M->myId_) return;
//   int senderid = lookupRatId(pack->id);
//   if (idExist(senderid))
//     if (M->last_seq[senderid] < pack->seqnum)
//       M->last_seq[senderid] = pack->seqnum;
//     else return; // sequence number out of order
//   
//   switch(pack->type) {
// 	case JOIN_REQUEST_TYPE:
// 	{
// 	  JoinRequestPack *packX = (JoinRequestPack *) &pack->body;
// 	
// 	  if (idExist(senderid))
// 	  {
// 	    sendPacketToPlayer(makeJoinResponsePack(pack->id));
// 	    break; // already in list
// 	  }
// 	    
// 	  // add the new rat into my rat list
// 	  if (addRat(pack->id, packX->name, pack->seqnum))
// 	  {
// 	    int newid = lookupRatId(pack->id);
// 	    joining[newid] = true;
// 	    sendPacketToPlayer(makeJoinResponsePack(pack->id));
// 	  }
// 
// 	  break;
// 	}
// 	
// 	case JOIN_RESPONSE_TYPE:
// 	{
// 	  JoinResponsePack *packX = (JoinResponsePack *) &pack->body;
// 	  
// 	  if (idExist(senderid) || (packX->target_id != M->myId_)) break; // already in list
// 	  
// 	  // add this responder into my rat list
// 	  if (addRat(pack->id, packX->name, pack->seqnum))
// 	  {
// 	    int newid = lookupRatId(pack->id);
// 	    uint16_t x,y,d;
// 	    getX_Y_D(packX -> xyd, x, y, d);
// 	    SetRatPosition(RatIndexType(newid), x, y, d);
// 	    M->scores_[newid] = packX->score;
// 	    joining[newid] = true;
// 	  }
// 	  break;
// 	}
// 	
// 	case UPDATE_TYPE:
// 	{
// 	  UpdatePack *packX = (UpdatePack *) &pack->body;
// 	  if (senderid < 0) break;
// 	  if(!M->rat(RatIndexType(senderid)).playing) 
// 	    if (joining[senderid]) joining[senderid] = false;
// 	    else break;
// 	  
// 	  // update the state of this responder in my rat list
// 	  uint16_t x,y,d;
// 	  getX_Y_D(packX -> xyd, x, y, d);
// 	  M->scores_[senderid] = packX->score;
// 	  UpdateScoreCard(RatIndexType(senderid));
// 	  if (sqOccupied(senderid, Loc(x), Loc(y)) < 0)
// 	    SetRatPosition(RatIndexType(senderid), x, y, d);
// 	  Rats2Display[senderid].visible = true;
// 	  
// 	  if ((x == M->xtag.value())&&(y == M->ytag.value())) hit = senderid;
// 	  break;
// 	}
// 	  
// 	case TAG_TYPE:
// 	{
// 	  TagPack *packX = (TagPack *) &pack->body;
// 	  if (!idExist(senderid)) break;
// 	  if (packX->target_id == M->myId_)
// 	  {
// 	    // check replicated tag packet
// 	    if ((lastAttackerId == pack->id) && abs((int)lastAttackSeqnum - (int)pack->seqnum) <= 5) break;
// 	    lastAttackerId = pack->id;
// 	    lastAttackSeqnum = pack->seqnum;
// 	    
// 	    NewPosition(M);
// 	    M->scoreIs(M->score().value()-5);
// 	    sendTagResponsePack(pack->id);
// 	    
// 	    UpdateScoreCard(RatIndexType(MY_RAT_INDEX));
// 	  }
// 	  break;
// 	}
// 	
// 	case TAG_RESPONSE_TYPE:
// 	{	  
// 	  TagResponsePack *packX = (TagResponsePack *) &pack->body;
// 	  if (!idExist(senderid)) break;
// 	  
// 	  // update the state of this responder in my rat list
// 	  uint16_t x,y,d;
// 	  getX_Y_D(packX -> xyd, x, y, d);
// 	  if (sqOccupied(senderid, Loc(x), Loc(y)) < 0)
// 	    SetRatPosition(RatIndexType(senderid), x, y, d);
// 	  M->scores_[senderid] = packX->score;
// 	  UpdateScoreCard(RatIndexType(senderid));
// 	  Rats2Display[senderid].visible = true;
// 	  
// 	  if (packX->attacker_id == M->myId_) 
// 	  {
// 	    tagDone[senderid] = true;
// 	    M->scoreIs(M->score().value()+11);
// 	    UpdateScoreCard(RatIndexType(MY_RAT_INDEX));
// 	    sendUpdatePack();
// 	  }
// 	  break;
// 	}
// 	
// 	case FINISH_TYPE:
// 	{
// 	  if (!idExist(senderid)) break;
// 	  removeRat(RatIndexType(senderid));
// 	  break;
// 	}
//   }
//       
//   timeval t;
//   gettimeofday(&t,NULL);
//   senderid = lookupRatId(pack->id);
//   if (idExist(senderid)) {
//     M->last_update_time[senderid] = t;
//   }
//     
//   updateView = TRUE;
//   DoViewUpdate();
//   gettimeofday(&pack_time, NULL);
//   last_time_out = false;
}


void* 
listenAddServerResponse(void * arg)
{
  MWEvent event;
  ShortPacket incoming;
  event.eventDetail = &incoming;

  // wait for Join Response
  while (my_state == WAIT_ADD_SERVER_RESPONSE)
  {
    NextEvent(&event, my_socket);
    if (checkSumRight(*event.eventDetail) && (event.eventSource.sin_addr.s_addr == wait_server_ip) && (event.eventDetail->header.type == ADD_SERVER_RESPONSE_TYPE))
    {
	my_state = INIT_STATE;
	rece_response = 1;printf( "receive server response!\n" );
	pthread_exit(NULL);
    }
  }
  pthread_exit(NULL);
}

void sendShortPacket(ShortPacket pack)
{
  if (rand()<RAND_MAX*PACK_LOST_RATE) return;
  static uint32_t my_seqnum = 0;
  pack.header.seqnum = my_seqnum;

  fillChecksum((LongPacket*)&pack);
//   ConvertOutgoing(&pack);
  
  if (sendto(my_socket, &pack, sizeof(pack), 0,
	      (struct sockaddr*) &groupAddr, sizeof(Sockaddr)) < 0)
    { MWError("Sample error"); }
  my_seqnum++;
}

void sendLongPacket(LongPacket pack)
{
  if (rand()<RAND_MAX*PACK_LOST_RATE) return;
  static uint32_t my_seqnum = 0;
  pack.header.seqnum = my_seqnum;

  fillChecksum(&pack);
//   ConvertOutgoing(&pack);
  
  if (sendto(my_socket, &pack, sizeof(pack), 0,
	      (struct sockaddr*) &groupAddr, sizeof(Sockaddr)) < 0)
    { MWError("Sample error"); }
  my_seqnum++;
}

void sendAddServerRequestPack()
{
  ShortPacket pack;
  AddServerRequestPack *packX;
  
  pack.header.type = ADD_SERVER_REQUEST_TYPE;
  pack.header.paylen = 4;
  
  packX = (AddServerRequestPack *) &pack.body;
  packX->ip = myIP;
  sendShortPacket(pack);
}


/* ------------------------------------------------------------------ */

int
AddServer( char * id ) {
#ifdef DEBUG
  printf( "AddServer: Server id %s\n", id );
#endif

  /****************************************************/
  /* Add server id to the list of servers             */
  /****************************************************/
  in_addr_t hostIP;
  Sockaddr *thisHost;
  if ((thisHost = resolveHost(id)) == (Sockaddr *) NULL) {
    printf("Hostname not exist.\n");
    return( NormalReturn );
  }
  hostIP = thisHost->sin_addr.s_addr;
#ifdef DEBUG
  printf( "Server ip %d\n", hostIP );
#endif

  my_state = WAIT_ADD_SERVER_RESPONSE;
  rece_response = 0;
  wait_server_ip = hostIP;
  
  pthread_t thread;
  int rc = pthread_create(&thread, NULL, listenAddServerResponse, NULL);
  
  int i;
  for (i = 0; i < MAX_TRIALS; i++) {
    printf("Wait for response.\n");
    sendAddServerRequestPack();
    usleep(RETRIAL_TIME);
    
    if (rece_response) {
      break;
    }
  }
  my_state = INIT_STATE;
  
  if (rece_response)
  {
    serverList[serverNum].addr = hostIP;
    serverList[serverNum].active = 1;
    serverNum++;
  }
  else
  {
    printf("Add Server Fail.\n");
  }
  
  return( NormalReturn );
}

/* ------------------------------------------------------------------ */

int
OpenFile( char * fileName ) {
  int fd;

  ASSERT( fileName );

#ifdef DEBUG
  printf( "OpenFile: Opening File '%s'\n", fileName );
#endif

  fd = open( fileName, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR );

#ifdef DEBUG
  if ( fd < 0 )
    perror( "OpenFile" );
#endif

  return( fd );
}

/* ------------------------------------------------------------------ */

int
WriteBlock( int fd, char * buffer, int byteOffset, int blockSize ) {
  //char strError[64];
  int bytesWritten;

  ASSERT( fd >= 0 );
  ASSERT( byteOffset >= 0 );
  ASSERT( buffer );
  ASSERT( blockSize >= 0 && blockSize < MaxBlockLength );

#ifdef DEBUG
  printf( "WriteBlock: Writing FD=%d, Offset=%d, Length=%d\n",
	fd, byteOffset, blockSize );
#endif

  if ( lseek( fd, byteOffset, SEEK_SET ) < 0 ) {
    perror( "WriteBlock Seek" );
    return(ErrorReturn);
  }

  if ( ( bytesWritten = write( fd, buffer, blockSize ) ) < 0 ) {
    perror( "WriteBlock write" );
    return(ErrorReturn);
  }

  return( bytesWritten );

}

/* ------------------------------------------------------------------ */

int
Commit( int fd ) {
  ASSERT( fd >= 0 );

#ifdef DEBUG
  printf( "Commit: FD=%d\n", fd );
#endif

	/****************************************************/
	/* Prepare to Commit Phase			    */
	/* - Check that all writes made it to the server(s) */
	/****************************************************/

	/****************/
	/* Commit Phase */
	/****************/

  return( NormalReturn );

}

/* ------------------------------------------------------------------ */

void
Abort( int fd )
{
  ASSERT( fd >= 0 );

#ifdef DEBUG
  printf( "Abort: FD=%d\n", fd );
#endif

  /*************************/
  /* Abort the transaction */
  /*************************/

  return;
}

/* ------------------------------------------------------------------ */

int
CloseFile( int fd ) {

  ASSERT( fd >= 0 );

#ifdef DEBUG
  printf( "Close: FD=%d\n", fd );
#endif

	/*****************************/
	/* Check for Commit or Abort */
	/*****************************/

  if ( close( fd ) < 0 ) {
    perror("Close");
    return(ErrorReturn);
  }

  return(NormalReturn);
}

/* ------------------------------------------------------------------ */




